/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "arch/asm_support.h"

// void InterpreterToCompiledCodeBridgeDyn(
//       const BytecodeInstruction* insn,       %rdi
//       const Frame *iframe,                   %rsi
//       const Method*,                         %rdx
//       ManagedThread* thread)                 %rcx
.global InterpreterToCompiledCodeBridgeDyn
TYPE_FUNCTION(InterpreterToCompiledCodeBridgeDyn)
InterpreterToCompiledCodeBridgeDyn:
    CFI_STARTPROC
    CFI_DEF_CFA(rsp, 8)

    pushq %rsi // iframe*
    CFI_ADJUST_CFA_OFFSET(8)

    movq %rsp, %rax
    CFI_DEF_CFA_REGISTER(rax)

    // According to the current frame kind set the bridge type
    movb MANAGED_THREAD_FRAME_KIND_OFFSET(%rcx), %r10b
    testb %r10b, %r10b
    movq $INTERPRETER_TO_COMPILED_CODE_BRIDGE, %r11
    movq $BYPASS_BRIDGE, %r10
    cmovne %r10, %r11
    pushq %r11

    pushq %rbp
    CFI_REL_OFFSET(rbp, -(2 * 8))

    movq %rax, %rbp // set frame pointer
    CFI_DEF_CFA_REGISTER(rbp)

    pushq %THREAD_REG
    CFI_REL_OFFSET(r15, -(3 * 8))
    pushq %r14
    CFI_REL_OFFSET(r14, -(4 * 8))
    pushq %r13
    CFI_REL_OFFSET(r13, -(5 * 8))
    pushq %r12
    CFI_REL_OFFSET(r12, -(6 * 8))
    pushq %rbx
    CFI_REL_OFFSET(rbx, -(7 * 8))

    pushq %rcx // thread*
    subq $16, %rsp
    // %rsp should be 16-byte aligned here

    // setup regs as follow
    // %rax - insn_ptr, %rbx - frame.vregs, %r12 - method, %r13, %r14 - temp, %r15 - acc
    movq %rdi, %rax // insn*
    leaq FRAME_VREGS_OFFSET(%rsi), %rbx // frame.vregs
    movq FRAME_ACC_OFFSET(%rsi), %r15 // acc
    movq %rdx, %r12 // method

    // the dispatch routine expects opcode in ecx
    movzbl (%rax), %ecx
    cmpl $MIN_PREFIX_OPCODE_INDEX, %ecx
    jl .Lnonprefixed
    movzwl (%rax), %ecx
    addq $2, %rax
    jmp .Ldispatch
.Lnonprefixed:
    addq $1, %rax // read opcode and advance insn_ptr

.Ldispatch:
    // The file contains code which checks opcode and jumps
    // to the corresponding handler.
    // At the end each handler jumps to .Lload_reg_args label.
    // The file is autogenerated from runtime/templates/bridge_dispatch.S.erb
    // Handlers are distinguished by format and located in the corresponding files with name:
    // handle_call_<format>.S
    // If you get a compilation error that there is no such file it seems
    // new call format was introduced and you have to implement the corresponding handler.
#include "bridge_dispatch_dyn_amd64.S"

    // invoke the method
    // since the first argument is Method* it must be in %rdi
.Linvoke:
    movq METHOD_COMPILED_ENTRY_POINT_OFFSET(%rdi), %rax
    movq -64(%rbp), %THREAD_REG
    callq *%rax
    // sp may be modified by call

    // handle the result
    // setup registers as follow
    // %rax, %rdx - result, %r12 - frame.acc, %rcx - temp
    movq (%rbp), %r12 // load iframe from the stack
    addq $FRAME_ACC_OFFSET, %r12

    // store value to frame.acc
    movq %rax, (%r12)

.Lreturn:
    leaq -56(%rbp), %rsp
    popq %rbx
    CFI_RESTORE(rbx)
    popq %r12
    CFI_RESTORE(r12)
    popq %r13
    CFI_RESTORE(r13)
    popq %r14
    CFI_RESTORE(r14)
    popq %THREAD_REG
    CFI_RESTORE(r15)
    popq %rbp
    CFI_RESTORE(rbp)
    CFI_DEF_CFA(rsp, (3 * 8))
    addq $16, %rsp
    CFI_ADJUST_CFA_OFFSET(-(2 * 8))
    retq
    CFI_ENDPROC

// void InvokeCompiledCodeWithArgArrayDyn(
//       const coretypes::TaggedValue* values,      %rdi
//       uint32_t num_args,                     %rsi
//       const Frame *iframe,                   %rdx
//       const Method*,                         %rcx
//       ManagedThread* thread)                 %r8
.global InvokeCompiledCodeWithArgArrayDyn
TYPE_FUNCTION(InvokeCompiledCodeWithArgArrayDyn)
InvokeCompiledCodeWithArgArrayDyn:
    CFI_STARTPROC
    CFI_DEF_CFA(rsp, 8)

    pushq %rdx // iframe*
    CFI_ADJUST_CFA_OFFSET(8)

    movq %rsp, %rax
    CFI_DEF_CFA_REGISTER(rax)

    // According to the current frame kind set the bridge type
    movb MANAGED_THREAD_FRAME_KIND_OFFSET(%r8), %r10b
    testb %r10b, %r10b
    movq $INTERPRETER_TO_COMPILED_CODE_BRIDGE, %r11
    movq $BYPASS_BRIDGE, %r10
    cmovne %r10, %r11
    pushq %r11

    pushq %rbp
    CFI_REL_OFFSET(rbp, -(2 * 8))

    movq %rax, %rbp // set frame pointer
    CFI_DEF_CFA_REGISTER(rbp)

    pushq %THREAD_REG
    CFI_REL_OFFSET(r15, -(3 * 8))

    pushq %r12
    CFI_REL_OFFSET(r12, -(4 * 8))
    pushq %rbx
    CFI_REL_OFFSET(rbx, -(5 * 8))
    subq $8, %rsp
    // %rsp should be 16-byte aligned here

    //  const coretypes::TaggedValue* values,      %rdi
    //  uint32_t num_args,                     %rsi
    //  const Frame *iframe,                   %rdx
    //  const Method*,                         %rcx
    //  ManagedThread* thread                  %r8

    movq %r8, %THREAD_REG // Properly set thread register

    // rax <- pointer to current arg to copy
    movq %rdi, %rax

    // ABI arg reg 0 (rdi) <- panda::Method*
    // ABI arg reg 1 (rsi) <- num_args (already on the relevant reg, no moves required)
    movq %rcx, %rdi

    // No arguments passed to the callee
    test %esi, %esi
    jz .Linvoke_

    // Allocate stack args
    leal (, %esi, FRAME_VREGISTER_SIZE), %r12d
    subq %r12, %rsp
    andq $-16, %rsp

    // Copy boxed arguments to the stack
    // ebx <- loop counter
    // rdx <- temp register
    leal -FRAME_VREGISTER_SIZE(, %esi, FRAME_VREGISTER_SIZE), %ebx
.Lloop_:
    movq (%rax, %rbx), %rdx
    movq %rdx, (%rsp, %rbx)
    subl $FRAME_VREGISTER_SIZE, %ebx
    jae .Lloop_

.Linvoke_:
    // invoke the entrypoint
    movq METHOD_COMPILED_ENTRY_POINT_OFFSET(%rdi), %rax
    callq *%rax
    // sp may be modified by call

    leaq -40(%rbp), %rsp
    popq %rbx
    CFI_RESTORE(rbx)
    popq %r12
    CFI_RESTORE(r12)
    popq %THREAD_REG
    CFI_RESTORE(r15)
    popq %rbp
    CFI_RESTORE(rbp)
    CFI_DEF_CFA(rsp, (3 * 8))
    addq $16, %rsp
    CFI_ADJUST_CFA_OFFSET(-(2 * 8))
    retq
    CFI_ENDPROC
